



#include <FL/fl_ask.H>
#include <algorithm>
#include "GenericPlugin.h"

GenericPlugin::GenericPlugin()
{
    slaveId = 0;
    pMaster = this; //this gets changed in a moment if is a slave
    params = NULL;
    bIsSlave = false;
    bIsPolyphonic = false;
}

void GenericPlugin::preInitMaster()
{
    for (int i=0; i<numOfParams; i++)
    {
        Envelope * e = new Envelope();
        e->points = new  multimap<double, double>;
        //e->points = new  vector<PointClass>;
//        songEnvelopes.push_back(e);
    }
    initMaster();
}


void GenericPlugin::removeInactiveSlaves()
{
    for (int i=0; i<slaves.size(); i++)
        if (slaves[i]->bIsFree)
        {
            delete(slaves[i]);
            slaves.erase(slaves.begin()+i);
        }
}


/*
struct compareOffsets
{
    bool operator()(pluginEvent const* lhs, pluginEvent const* rhs) const
    {
        return lhs->sampleOffset < rhs->sampleOffset;
    }
};
*/

void GenericPlugin::preProcessBuffer( float *outputBuffer, unsigned long framesPerBuffer, unsigned long totalSamplesIn)
{
    //current events should already be sorted so we don't need to sort any more.
      //std::sort(currentEvents.begin(), currentEvents.end(), compareOffsets());

    long location = 0;
    long samplesToProcess = 0;
    //process the buffer up to the next event ...
    for (int i=0; i<currentEvents.size(); i++)
    {
        //fl_alert("processing an event. of %d", currentEvents.size());
/*
            if (currentEvents[i]->eventType == EVT_NOTE_OFF)
                fl_alert("note offG");
*/
        if (currentEvents[i]->sampleOffset < framesPerBuffer)
        {
            samplesToProcess = currentEvents[i]->sampleOffset - location;

            handleEvent(currentEvents[i]);
            totalSamples = totalSamplesIn;
            processBuffer( &outputBuffer[location*2], samplesToProcess);
            location +=  samplesToProcess;
            totalSamples += samplesToProcess;
            //now remove the event from the currentEvents vector as it has just been processed
            currentEvents.erase (currentEvents.begin()+i);
        }
        else //don't do this event now, but adjust it's sample offset so that it can maybe be done next time..
            currentEvents[i]->sampleOffset -= framesPerBuffer;
    }
    totalSamples = totalSamplesIn;
    processBuffer( &outputBuffer[location*2], framesPerBuffer - location);
    isFree(); //sets free flag it free;
    if (bIsFree) slaveId = -2;
    //currentEvents.clear();
}

GenericPlugin* GenericPlugin::getAFreeSlave(int noteId) //creates a slave if none is found free...
{
  /*  bool bFound = false;
    int i;
    for (i=0; i<slaves.size(); i++)
        if (slaves[i]->bIsFree)
        {
            bFound = true;
            break;//return slaves[i];
        }

    if (bFound)
        return slaves[i];
    else //create a slave.*/
    {
        newSlave();
        slaves.back()->bIsSlave = true;
        slaves.back()->pMaster = pMaster;
        slaves.back()->slaveId = noteId;


        //slaves.back()->pGenericNoteEnvelopes = pGenericNoteEnvelopes;
        //slaves.back()->songEnvelopes = songEnvelopes;
        for (int i=0; i<numOfParams; i++)
        {
//            slaves.back()->songEnvelopes.push_back(new Envelope());
    //        slaves.back()->songEnvelopes.back() = songEnvelopes[i];
        }

        slaves.back()->params = params;
        //slaves.back()->init();
        //pMaster->slaveId++;
        return slaves.back(); //should return newly created slave
    }
}



void GenericPlugin::addEvent(pluginEvent* event)
{
    bool bInserted = false;

    //if (slaves.size() > 20) fl_alert("too many slaves");

    //add the event in the right place according to sampleOffset
    //so go through the events and find where

 //   if (event->eventType == EVT_NOTE_OFF) fl_alert("note off aE");
 //   if (bIsSlave == false) fl_alert("nmaster aE, slaveId = %d", slaveId);

    /*if (currentEvents.size() == 0) //if there are no events just add it to the end
        currentEvents.push_back(event);
    else*/

        for (int i=0; i<currentEvents.size(); i++)
           //currentEvents should be sorted so that the lowest sampleOffset is first
            if (currentEvents[i]->sampleOffset > event->sampleOffset)
            {
                currentEvents.insert( currentEvents.begin() + i , event);
                bInserted = true;
                break;
            }


    if (!bInserted)
        currentEvents.push_back(event);
    //if we've added an event then make the plugin not free (maybe change this)
    bIsFree = false;
}
/*
struct comparePoints
{
    bool operator()(Point const lhs, Point const rhs) const
    {
        return lhs.time < rhs.time;
    }
};
*/

double GenericPlugin::getValue(unsigned int param)
{
    return 8.0;
    //fl_alert("%f", songEnvelopes[param]->getValue(totalSamples));
    //return songEnvelopes[param]->getValue(totalSamples);

    //somehow increment the envelope positions
    //straight line can represented by something like y = 2x + 5;
    //envelope equations



  //  std::upper_bound(songEnvelopes[param]->points.begin(), songEnvelopes[param]->points.end(), comparePoints());


/*
    if ((totalSamples >= envelopeEquations[param].validFrom) && (totalSamples > envelopeEquations[param].validTo || envelopeEquations[param].validTo == -1))
    { //then the equation we have is valid
        return envelopeEquations[param].coeff * totalSamples + envelopeEquations[param].offset;
    }
    //otherwise find where we are in the points vector and recalculate equation

    envelopeEquations[param].nextPointIndex=0;
    while ( envelopeEquations[param].nextPointIndex < songEnvelopes[param]->points.size() && songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].time < totalSamples )
    {
        envelopeEquations[param].nextPointIndex++;
    }

    if ( envelopeEquations[param].nextPointIndex >= songEnvelopes[param]->points.size() )
    { //then we've reached the end of the envelope..
        envelopeEquations[param].coeff = 0;
        envelopeEquations[param].offset = songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex-1].value;

        envelopeEquations[param].validFrom = songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex-1].time;
        envelopeEquations[param].nextPointIndex = -1;
        envelopeEquations[param].validTo = -1;
    }
    else
    {
        double c;
        double o;
        c = (songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].value - songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex-1].value) /  (songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].time - songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex-1].time) ;
        o = songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].value - c*songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].time;
        envelopeEquations[param].coeff = c;
        envelopeEquations[param].offset = o;
        envelopeEquations[param].validTo = songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex].time;
        envelopeEquations[param].validFrom = songEnvelopes[param]->points[envelopeEquations[param].nextPointIndex-1].time;
    }


    return envelopeEquations[param].coeff * totalSamples + envelopeEquations[param].offset;
*/
    //songEnvelopes[param]->getValue(totalSamples);
}




//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////









GenericPluginRegistry& GenericPluginRegistry::get()
{
    static GenericPluginRegistry instance;
    return instance;
}

void GenericPluginRegistry::add(genericPlugin_creator creator)
{
    m_genericPlugins.push_back(creator);
}

GenericPluginRegistry::iterator GenericPluginRegistry::begin()
{
    return m_genericPlugins.begin();
}

GenericPluginRegistry::iterator GenericPluginRegistry::end()
{ return m_genericPlugins.end(); }

GenericPluginRegistration::GenericPluginRegistration(genericPlugin_creator creator)
{
    GenericPluginRegistry::get().add(creator);
}
